/*
 * TreeView.scala
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package net.liftweb.widgets.trees.treeview

import scala.collection.mutable.HashMap
import scala.xml.{NodeSeq, Node, PCData, Text, Unparsed, NodeBuffer, Elem, MetaData, Null, TopScope}

import java.lang.reflect.Method

import net.liftweb.widgets.trees.model.{TreeModel,NodeInfo}
import net.liftweb.widgets.trees.util.BinderMethod

/*
 * 
 */
 
class TreeViewOptions {
}


/**
 * widget based on jQuery Treeview Plugin (http://jquery.bassistance.de/treeview/demo/)
 */

object TreeView {
  /**
   * register the resources with lift (typically in boot)
   */
   
  def init() {  
    import net.liftweb.http.ResourceServer  
   
    ResourceServer.allow({  
      case "treeview" :: _ => true  
    })
    
    println("register tree-view")
  }

  /**
   * render only the javascript and the links to the css
   *
   * @param id the id of the &lt;ud&gt; tag
   */
  
  def render (id : String, treeviewOptions : TreeViewOptions)
    : NodeSeq =
  {
      val js = renderJs (id)

      <xml:group>
      <head>
        <link href="/classpath/treeview/themes/default/jquery.treeview.css" type="text/css" rel="stylesheet" />
        <script type="text/javascript" src="/classpath/treeview/jquery.treeview.js"></script>
        
        <script type="text/javascript">
            {Unparsed (js)}
        </script>
      </head>
      </xml:group>
  }

  /**
   * render a tree-view using jQuery Treeview Plugin (http://jquery.bassistance.de/treeview/demo/)
   *
   * this version a in-memory model.
   */
  
  def render [A <: AnyRef, B <: AnyRef] (treeModel : TreeModel [A, B], treeviewOptions : TreeViewOptions, ulsBinder : Function2[NodeSeq, Nothing, NodeSeq]*) 
    : (NodeSeq => NodeSeq) =
  {
    // TODO: infer from template or generate automatically
    val id = "abc"
    
    // map the type of row with the "row binder" object and method
    val binderMethod = new BinderMethod (ulsBinder : _*)
    val templates = new HashMap [String, Node] ()

    //
    
    def renderNode [X <: AnyRef, Y <: AnyRef] (tm : TreeModel [X, Y]) : Node = {
      //
      val rowType = BinderMethod.rowTypeKey (tm.getNode.getClass)

      try {
        // search the row template
        val template = templates (rowType)

        // search the row-binder function
        val binder = binderMethod.get (rowType)

        val parameters = new Array [Object] (2)
        parameters (0) = template
        parameters (1) = tm.getNode
          
        // invoke the row binder
        val nodeSeq = binder._2.invoke (binder._1, parameters).asInstanceOf [NodeSeq]
        val node = nodeSeq.elements.next
        
        val childs = node.child ++ renderLeafs (tm.getLeafs)

        val elem = Elem (null, node.label,
              node.attributes,
              TopScope,
              childs : _*
              )

        elem
      }
      catch {
        case nseex : NoSuchElementException  => 
          val msg = "Either template or binder not defined for row type " + rowType
          println (msg)
          <li>{msg}</li>
      }
    }
    
    //
    def renderLeafs [X <: AnyRef, Y <: AnyRef] (leafs : List [TreeModel [X, Y]]) : Node = {
      leafs match {
        case Nil => Text ("")
        case z =>
          val zs = new NodeBuffer();
      
          for (tm <- leafs)
           zs &+ renderNode (tm)
      
          <ul>{zs}</ul>
      }
    }
    
    //
    def transformLi (xhtml : NodeSeq) : NodeSeq = {
      // TODO: parse XHTML to find <li> templates

      for (val li <- xhtml) {
        li match {
          case rowXml @ <li>{ ns @ _* }</li> if rowXml.attribute("rowtype").isDefined =>
                  rowXml.attribute("rowtype").elements.next match {
                      case Text (rowType) => 
                        val elem = Elem(null, "li", rowXml.attributes.remove ("rowtype"), TopScope, ns :_*)
                        templates += (rowType-> elem)
                      case _ => println ("rowtype attribute is defined but no contains value " + li)
                  }
                  
          case _ => println (li) ; ()
        }
      }

      println (templates)
      
      //
      val zs = new NodeBuffer();
      
      for (tm <- treeModel.getLeafs)
        zs &+ renderNode (tm)

      zs
    }

    //
    def transformOneNode (n: Node):Iterable[Node] = {
      n match {
        case x @ <ul>{ ns @ _* }</ul> =>
            <ul id="abc">{transformLi (ns)}</ul>
        case Elem(namespace, label, attrs, scp, ns @ _*) =>
            Elem(namespace, label, attrs, scp, transform( ns ):_* )
        case z =>
            z 
      }
    }

    //
    def transform (xhtml : NodeSeq) : NodeSeq = {
      val zs = new NodeBuffer();
      for(val z <- xhtml) {zs &+ transformOneNode (z)}

      zs
    }
    
    //
    def ret (xhtml : NodeSeq) : NodeSeq = {
      val js = renderJs (id)
    
      <xml:group>
      <head>
        <link href="/classpath/treeview/themes/default/jquery.treeview.css" type="text/css" rel="stylesheet" />
        <script type="text/javascript" src="/classpath/treeview/jquery.treeview.js"></script>
        
        <script type="text/javascript">
        {Unparsed (js)}
        </script>
      </head>
      {transform (xhtml)}
      </xml:group>
    }

    ret
  }
  
  private def renderJs (id : String) : String = {
    var sz = new StringBuilder (500)
    
    sz.append ("""
/* <![CDATA[ */		      
jQuery(function(){
  """)
  
    sz.append ("  jQuery(\"#" + id +"\").treeview () ;") ;
    
    sz.append ("""  
});
/* ]]> */    
    """)

    sz.toString 
  }
}
